#ifndef _LINUX_MMZONE_H
#define _LINUX_MMZONE_H

#ifdef __KERNEL__
#ifndef __ASSEMBLY__

#include <linux/config.h>
#include <linux/spinlock.h>
#include <linux/list.h>

/*
 * Free memory management - zoned buddy allocator.
 * 伙伴分配器的核心就是将N个页面组成一个伙伴，分配时一起将这些内存分配出去。
 */

#define MAX_ORDER 10

/* 描述和管理空闲页信息 */
/* 该结构是zone形式内存分配的核心，一个free_area_struct就表示一个连续物理内存块
 * 一个free_area_struct就可以表示一个连续为1页或者2页或者...10个页的连续物理内存块
 * 即zone_struct结构体管理的就是free_area_struct代表的内存块
 * free_list的作用就是将相同大小的free_area_struct内存块连接管理起来
 * map字段的作用就是记录内存块的使用情况
 * 究竟一个free_area_struct结构体代表是一个物理页还是10个物理页，通过的是zone_struct的free_area_t free_ara[MAX_ORDER]成员来决定的
 * 通过free_area[下标] 数组的下标，比如free_area[0]中全部都是一个页面的free_area_struct，同理free_area[1]就是全部都是2个页面的。。。。
 */
typedef struct free_area_struct {
	struct list_head	free_list;
	unsigned int		*map;  /* map是一个位图，用于记录内存的使用情况 */
} free_area_t;

struct pglist_data;

typedef struct zone_struct {
	/*
	 * Commonly accessed fields:
	 */
	spinlock_t		lock;
	unsigned long		offset;             /* 表示该分区在物理内存中的起始编号，由于zone的地址是连续的，所以通过offset就可以判断区域 */
	unsigned long		free_pages;
	unsigned long		inactive_clean_pages;
	unsigned long		inactive_dirty_pages;
	unsigned long		pages_min, pages_low, pages_high;

	/*
	 * free areas of different sizes
	 */
	struct list_head	inactive_clean_list;
	free_area_t		free_area[MAX_ORDER];           /* 根据下标管理不同页面大小的free_area_t成员 */

	/*
	 * rarely used fields:
	 */
	char			*name;
	unsigned long		size;
	/*
	 * Discontig memory support fields.
	 */
	struct pglist_data	*zone_pgdat;                /* 指向所属的存储节点 */
	unsigned long		zone_start_paddr;
	unsigned long		zone_start_mapnr;
	struct page		*zone_mem_map;
} zone_t;

#define ZONE_DMA		0
#define ZONE_NORMAL		1
#define ZONE_HIGHMEM		2
#define MAX_NR_ZONES		3

/*
 * One allocation request operates on a zonelist. A zonelist
 * is a list of zones, the first one is the 'goal' of the
 * allocation, the other zones are fallback zones, in decreasing
 * priority.
 *
 * Right now a zonelist takes up less than a cacheline. We never
 * modify it apart from boot-up, and only a few indices are used,
 * so despite the zonelist table being relatively big, the cache
 * footprint of this construct is very small.
 */
typedef struct zonelist_struct {
	zone_t * zones [MAX_NR_ZONES+1]; // NULL delimited
	int gfp_mask;
} zonelist_t;

#define NR_GFPINDEX		0x100

struct bootmem_data;
/*
 * 一个pglist_data代表一个存储节点。存储节点是在物理上独立的，比如两块内存卡，就是两个存储节点
 * 存储节点在物理上是独立的，但是在内存线性地址是连续的，那为什么地址连续的存储节点还要进行区分呢?
 * 使用存储节点的作用是，比如分配一块4M的内存，在存储节点1上只能分配3M，在存储节点2上分配1M
 * 虽然这种分配貌似合理，但是却会带来很多问题，比如多核处理器对内存的竞争访问，不同内存访问速度等等
 * 因此，显然理论上是可行的，但在实际使用中，却倾向于在同一块内存中分配存储
 */
typedef struct pglist_data {
	zone_t node_zones[MAX_NR_ZONES];            /* 存放被节点的区域信息，在同一存储节点上，可以按区域划分，比如DMA一个区域，NORMAL一个区分，但不一定要分多个 */
	zonelist_t node_zonelists[NR_GFPINDEX];     /* 存放内存中所有存储节点的分区方案，先从本节点进行分配，不行的话再从其他存储节点进行分配，保存所有存储节点的优点是可以方便地从备用存储节点分配*/
	struct page *node_mem_map;                  /* 指向page数组结构，即指向该节点的物理页数组 */
	unsigned long *valid_addr_bitmap;
	struct bootmem_data *bdata;
	unsigned long node_start_paddr;
	unsigned long node_start_mapnr;
	unsigned long node_size;
	int node_id;
	struct pglist_data *node_next;              /* 存储节点单链表结构 */
} pg_data_t;

extern int numnodes;
extern pg_data_t *pgdat_list;

#define memclass(pgzone, tzone)	(((pgzone)->zone_pgdat == (tzone)->zone_pgdat) \
			&& (((pgzone) - (pgzone)->zone_pgdat->node_zones) <= \
			((tzone) - (pgzone)->zone_pgdat->node_zones)))

/*
 * The following two are not meant for general usage. They are here as
 * prototypes for the discontig memory code.
 */
struct page;
extern void show_free_areas_core(pg_data_t *pgdat);
extern void free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
  unsigned long *zones_size, unsigned long paddr, unsigned long *zholes_size,
  struct page *pmap);

extern pg_data_t contig_page_data;

#ifndef CONFIG_DISCONTIGMEM

#define NODE_DATA(nid)		(&contig_page_data)
#define NODE_MEM_MAP(nid)	mem_map

#else /* !CONFIG_DISCONTIGMEM */

#include <asm/mmzone.h>

#endif /* !CONFIG_DISCONTIGMEM */

#define MAP_ALIGN(x)	((((x) % sizeof(mem_map_t)) == 0) ? (x) : ((x) + \
		sizeof(mem_map_t) - ((x) % sizeof(mem_map_t))))

#endif /* !__ASSEMBLY__ */
#endif /* __KERNEL__ */
#endif /* _LINUX_MMZONE_H */
